package cn.jiangjiesheng.bootstrap.gateway.commons;

import cn.jiangjiesheng.bootstrap.gateway.auth.AuthFilterProperties;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpCookie;
import org.springframework.http.server.PathContainer;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Service;
import org.springframework.web.server.ServerWebExchange;

import java.util.Arrays;
import java.util.List;
import java.util.Objects;

/**
 * Created by dev@jiangjiesheng.cn on 2022/9/14  <br/>
 *
 * @author dev@jiangjiesheng.cn
 */
@Slf4j
@Service
public class CommonService {


    @Autowired
    private AuthFilterProperties authFilterProperties;

    /**
     * 是否是静态文件
     *
     * @param exchange
     * @return
     */
    public Boolean isStatic(ServerWebExchange exchange) {
        // 判断当前是否是静态文件
        final String reqPathString = exchange.getRequest().getPath().value();
        final String[] pathSplits = reqPathString.split("\\.");
        log.debug("path split by . : raw {} after {}", reqPathString, pathSplits);
        if (pathSplits.length > 1 &&
                Arrays.asList("js", "css", "woff", "woff", "ico", "jpg", "jpeg", "webp", "png", "gif").contains(pathSplits[pathSplits.length - 1])
                || reqPathString.contains("actuator")) {
            return true;
        }
        return false;
    }

    /**
     * 检查是否是健康检查
     *
     * @param exchange
     * @return
     */
    public Boolean isHealthCheck(ServerWebExchange exchange) {
        return exchange.getRequest().getPath().value().contains("actuator");
    }


    /**
     * 获取cookie值
     *
     * @param request
     * @param key
     * @return
     */
    public String readCookieValue(ServerHttpRequest request, String key) {
        final HttpCookie first = request.getCookies().getFirst(key);
        if (Objects.isNull(first)) {
            return null;
        }
        return first.getValue();
    }

    public String readHeaderValue(ServerHttpRequest request, String key) {
        final List<String> values = request.getHeaders().get(key);
        if (Objects.isNull(values)) {
            return "";
        }
        for (String value : values) {
            if (StringUtils.isNotBlank(value)) return value;
        }
        return "";
    }

    /**
     * 判断对应路由是否需要登录
     *
     * @param req
     * @return
     */
    private boolean inExcludeFilter(ServerHttpRequest req) {
        String userPath = req.getPath().value();
        final String serviceName = readServiceName(req);
        log.debug("check exclude service {}", serviceName);
        if(Objects.isNull(serviceName)){
            return true;
        }
        if (Objects.nonNull(authFilterProperties) &&
                Objects.nonNull(authFilterProperties.getExcludeService()) &&
                authFilterProperties.getExcludeService().contains(serviceName)) {
            return true;
        }
        if (userPath.equals("/")) {
            return true;
        }
        for (String exclude : authFilterProperties.getExcludes()) {
            if (userPath.startsWith(exclude)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 判断是否跳过鉴权
     *
     * @param exchange
     * @return
     */
    public Boolean isSkipAuth(ServerWebExchange exchange) {
        if (inExcludeFilter(exchange.getRequest())) {
            log.debug("path过滤器放行 {}", exchange.getRequest().getPath().value());
            return true;
        }
        // 健康检查放行
        if (isHealthCheck(exchange)) return true;
        // 静态文件放行
        if (isStatic(exchange)) return true;
        return false;
    }

    /**
     * 从路由中读取服务名
     *
     * @param req
     * @return
     */
    public String readServiceName(ServerHttpRequest req) {
        log.debug("path {}",req.getPath().value());
        final int countMatches = StringUtils.countMatches(req.getPath().value(), "/");
        if (countMatches <= 1) {
            return "";
        }
        final PathContainer subPath = req.getPath().subPath(2, 4);
        return StringUtils.stripStart(subPath.value(), "/");
    }
}
